package
{
	import flash.display.DisplayObject;
	import flash.events.MouseEvent;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.ui.Mouse;
	
	import mx.core.ILayoutElement;
	import mx.core.IVisualElement;
	import mx.core.UIComponent;
	import mx.effects.Move;
	import mx.states.Transition;
	
	import spark.components.supportClasses.GroupBase;
	import spark.effects.AnimateTransform;
	import spark.effects.Scale;
	import spark.layouts.supportClasses.LayoutBase;
	
	public class FishEyeLayout extends LayoutBase
	{								
		private var _maxRows:Number = Infinity;
		private var _maxColumns:Number = Infinity;
		
		public function FishEyeLayout()
		{
			super();
		}
		
		public function set maxRows(value:Number):void
		{
			_maxRows = value;
		}

		public function set maxColumns(value:Number):void
		{
			_maxColumns= value;
		}
		
		private function distanceBetweenBlocks(b1:Point, b2:Point):Number
		{
			return Math.abs(b1.x - b2.x) + Math.abs(b1.y - b2.y);	
		}
		
		private function deviation(origine:Number, deviation:Number, max:Number):Number
		{
			var distance:Number = Math.abs(origine - deviation);
			
			var t:Number = 0;
			for (var i:Number = 0; i < distance; i++) {
				t += max * 1 / (i + 5);
			}

			if (origine - deviation < 0) {
				return t;
			} else {
				return -t;
			}
		}
		
		private function distance(p1:Point, p2:Point):Number
		{
			return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
		}
		
		private function distanceWithMouse(p:Point):Number
		{			
			return distance(p, new Point(target.contentMouseX, target.contentMouseY));
		}
		
		public function normalDistribution(x:Number, mean:Number, deviation:Number):Number
		{
			return Math.exp((mean - x) / (2 * deviation)) / (deviation * Math.sqrt(2 * Math.PI));
		}
		
		private function zoomWithDistanceBlock(distance:Number):Number
		{
			return 1.8 * normalDistribution(distance, 0, 0.5) / normalDistribution(0, 0, 0.5) + 0.3;
		}
		
		private function zoomWithDistance(distance:Number):Number
		{
			return 1.8 * normalDistribution(distance, 0, 50) / normalDistribution(0, 0, 100) + 0.3;
		}
		
		private function blockToId(block:Point, numRows:Number, numColumns:Number):Number
		{
			if (block.x < 0 || block.x >= numColumns) {
				return -1;
			}
			
			if (block.y < 0 || block.y >= numRows) {
				return -1;
			}
			
			return block.y * numColumns + block.x;
		}
		
	    override public function updateDisplayList(containerWidth:Number, containerHeight:Number):void
		{
			super.updateDisplayList(containerWidth, containerHeight);
						
			//make sure that target i.e container which is using this layout, is not null
			if (!target) {
				return;
			}
			
			var numRows:Number;
			var numColumns:Number;
			if (_maxRows <= _maxColumns) {
				numRows = Math.floor(Math.sqrt(target.numElements * containerHeight / containerWidth) + 0.5);
				if (numRows > _maxRows) {
					numRows = _maxRows;	
				}
				
				numColumns = Math.ceil(target.numElements / numRows);
			} else {
				numColumns = Math.floor(Math.sqrt(target.numElements * containerWidth / containerHeight) + 0.5);
				if (numColumns > _maxColumns) {
					numColumns = _maxColumns;
				}
				
				numRows = Math.ceil(target.numElements / numColumns);
			}
			
			var elementWidth:Number;
			var elementHeight:Number;
			
			var blockHeight:Number = containerHeight / numRows;
			var blockWidth:Number = containerWidth / numColumns;
			
			// Position de départ
			var x:Number = 0;
			var y:Number = -blockHeight;
			
			
			var focusedBlock:Point = new Point(Math.floor(numColumns * target.contentMouseX / containerWidth), Math.floor(numRows * target.contentMouseY / containerHeight));
			var currentBlock:Point = new Point(0, -1);
			var focusedBlockExists:Boolean = blockToId(focusedBlock, numRows, numColumns) != -1; //focusedBlock.y * numColumns + focusedBlock.x;
//			trace(focusedBlock, idFocusedBlock, target.numElements);
			for (var i:int = 0; i < target.numElements; i++) {
				var element:IVisualElement = target.getElementAt(i);
				
				if (i % numColumns == 0) {
					// On démarre tout à gauche
					x = 0;
					
					// Nouvelle ligne
					y += blockHeight;
					
					currentBlock.x = 0;
					currentBlock.y++;
				}
				
				// Calcul de la taille de l'élément
				if (blockHeight < blockWidth) {
					elementHeight = blockHeight / 2;
					elementWidth = elementHeight * element.getPreferredBoundsWidth() / element.getPreferredBoundsHeight();
				} else {
					elementWidth = blockWidth / 2;
					elementHeight = elementWidth * element.getPreferredBoundsHeight() / element.getPreferredBoundsWidth();
				}
				
				var m:Matrix = element.getLayoutMatrix();
				m.identity();
				
				// Positionnement au milieu du block
				m.translate(x + (blockWidth / 2) - (elementWidth / 2), y + (blockHeight / 2) - (elementHeight / 2));
				element.setLayoutMatrix(m, false);

				element.setLayoutBoundsSize(elementWidth, elementHeight);
				
				var elementPosition:Point = new Point(x + blockWidth / 2, y + blockHeight / 2);
				
//				var zoom:Number = zoomWithDistance(distanceWithMouse(elementPosition));
				var zoom:Number = zoomWithDistanceBlock(distanceBetweenBlocks(currentBlock, focusedBlock));
				m.translate(-elementPosition.x, -elementPosition.y)
				m.scale(zoom, zoom);
				if (!focusedBlockExists)  {
					trace("***");
					m.translate(elementPosition.x, elementPosition.y);
					trace(elementPosition.x, elementPosition.y);
				} else {
	
					//*
					m.translate(elementPosition.x + deviation(currentBlock.x, focusedBlock.x, blockWidth), elementPosition.y + deviation(currentBlock.y, focusedBlock.y, blockHeight));
					/*/
					m.translate(elementPosition.x, elementPosition.y);
					//*/	
				}
				
				element.setLayoutMatrix(m, false); 
				
				x += blockWidth;
				currentBlock.x++;
			}
		}
		
		public function onMouseMove(event:MouseEvent):void
		{
			target.invalidateDisplayList();
		}

		public function onRollOut(event:MouseEvent): void
		{
			target.invalidateDisplayList();
		}
	}
}